#!/www/server/panel/pyenv/bin/python
#coding: utf-8
# +-------------------------------------------------------------------
# | 宝塔Linux面板 
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# +-------------------------------------------------------------------
# | Author: hwliang <hwl@bt.cn>
# +-------------------------------------------------------------------
from gevent import monkey
monkey.patch_all()
import os,sys,ssl

_PATH = '/www/server/panel'
os.chdir(_PATH)

upgrade_file = 'script/upgrade_flask.sh'
if os.path.exists(upgrade_file):
    os.system("nohup bash {} &>/dev/null &".format(upgrade_file))
    
if os.path.exists('class/flask'):
    os.system('rm -rf class/flask')
if not 'class/' in sys.path:
    sys.path.insert(0,'class/')
from BTPanel import app,sys,public
is_debug = os.path.exists('data/debug.pl')


if is_debug:
    import pyinotify,time,logging,re
    logging.basicConfig(level=logging.DEBUG,format="[%(asctime)s][%(levelname)s] - %(message)s")
    logger = logging.getLogger()

    class PanelEventHandler(pyinotify.ProcessEvent):
        _exts = ['py','html','BT-Panel','so']
        _explude_patts = [
            re.compile('{}/plugin/.+\.html'.format(_PATH)),
            re.compile('{}/(tmp|temp)/.+'.format(_PATH)),
            re.compile('{}/pyenv/.+'.format(_PATH))
        ]
        _lsat_time = 0
        

        def is_ext(self,filename):
            fname = os.path.basename(filename)
            result = fname.split('.')[-1] in self._exts
            if not result: return False
            for e in self._explude_patts:
                if e.match(filename): return False
            return True

        def panel_reload(self,filename,in_type):
            stime = time.time()
            if stime - self._lsat_time < 2:
                return
            self._lsat_time = stime
            logger.debug('检测到文件: {} -> {}'.format(filename,in_type))
            
            fname = os.path.basename(filename)
            if fname in ['BT-Task']:
                logger.debug('正在后台任务...')
                if os.path.getsize(filename) < 4096:
                    public.ExecShell("{} {}/BT-Task".format(public.get_python_bin(),_PATH))
                else:
                    public.ExecShell("{}/BT-Task".format(_PATH))
                logger.debug('后台任务已启动!')
            else:
                logger.debug('正在重启面板...')
                public.ExecShell("bash {}/init.sh reload &>/dev/null &".format(_PATH))

        def process_IN_CREATE(self, event):
            if not self.is_ext(event.pathname): return
            self.panel_reload(event.pathname,'[创建]')

        def process_IN_DELETE(self,event):
            if not self.is_ext(event.pathname): return
            self.panel_reload(event.pathname,'[删除]')
        
        def process_IN_MODIFY(self,event):
            if not self.is_ext(event.pathname): return
            self.panel_reload(event.pathname,'[修改]')
        
        def process_IN_MOVED_TO(self,event):
            if not self.is_ext(event.pathname): return
            self.panel_reload(event.pathname,'[覆盖]')

    def debug_event():
        logger.debug('以debug模式启动面板')
        logger.debug('监听端口：0.0.0.0:{}'.format(public.readFile('data/port.pl')))

        event = PanelEventHandler()
        watchManager = pyinotify.WatchManager()
        mode = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY | pyinotify.IN_MOVED_TO
        watchManager.add_watch(_PATH, mode, auto_add=True, rec=True)
        notifier = pyinotify.Notifier(watchManager, event)
        notifier.loop()

if __name__ == '__main__':
    pid_file = "{}/logs/panel.pid".format(_PATH)
    if os.path.exists(pid_file):
        public.ExecShell("kill -9 {}".format(public.readFile(pid_file)))
    pid = os.fork()
    if pid: sys.exit(0)
    
    os.setsid()

    _pid = os.fork()
    if _pid:
        public.writeFile(pid_file,str(_pid))
        sys.exit(0)

    sys.stdout.flush()
    sys.stderr.flush()

    f = open('data/port.pl')
    PORT = int(f.read())
    HOST = '0.0.0.0'
    if os.path.exists('data/ipv6.pl'):
        HOST = "0:0:0:0:0:0:0:0"
    f.close()
    
    
    keyfile = 'ssl/privateKey.pem'
    certfile = 'ssl/certificate.pem'
    is_ssl = False
    if os.path.exists('data/ssl.pl') and os.path.exists(keyfile) and os.path.exists(certfile):
        is_ssl = True
    
    if not is_ssl or is_debug:
        err_f = open('logs/error.log','a+')
        os.dup2(err_f.fileno(),sys.stderr.fileno())
        err_f.close()

    import threading
    os.system("nohup {} {}/jobs.py &>/dev/null &".format(public.get_python_bin(),public.get_class_path()))
    import task
    p1 = threading.Thread(target=task.systemTask)
    p1.start()
    p2 = threading.Thread(target=task.check502Task)
    p2.start()
    p3 = threading.Thread(target=task.update_software_list)
    p3.start()

    if is_ssl:
        ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        ssl_context.load_cert_chain(certfile=certfile,keyfile=keyfile)
        if hasattr(ssl_context, "minimum_version"):
            ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
        else:
            ssl_context.options = (ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1)
        
        ssl_context.set_ciphers("ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE")
        is_ssl_verify = os.path.exists('/www/server/panel/data/ssl_verify_data.pl')
        if is_ssl_verify:
            crlfile = '/www/server/panel/ssl/crl.pem'
            rootcafile = '/www/server/panel/ssl/ca.pem'
            #注销列表
            # ssl_context.load_verify_locations(crlfile)
            # ssl_context.verify_flags |= ssl.VERIFY_CRL_CHECK_CHAIN
            #加载证书
            ssl_context.load_verify_locations(rootcafile)
            ssl_context.verify_mode = ssl.CERT_REQUIRED
            ssl_context.set_default_verify_paths()

    from  gevent.pywsgi import WSGIServer
    try:
        import flask_sock
        if is_ssl:
            http_server = WSGIServer((HOST, PORT), app,ssl_context = ssl_context)
        else:
            http_server = WSGIServer((HOST, PORT), app)
    except:
        from geventwebsocket.handler import WebSocketHandler
        if is_ssl:
            http_server = WSGIServer((HOST, PORT), app,ssl_context = ssl_context,handler_class=WebSocketHandler)
        else:
            http_server = WSGIServer((HOST, PORT), app,handler_class=WebSocketHandler)
            

    if is_debug:
        try:
            dev = threading.Thread(target=debug_event)
            dev.start()
        except:
            pass

    http_server.serve_forever()